// 给定一个机票的字符串二维数组 [from, to]，子数组中的两个成员分别表示飞机出发和降落的机场地点，对该行程进行重新规划排序。所有这些机票都属于一个从 JFK（肯尼迪国际机场）出发的先生，所以该行程必须从 JFK 开始。

// 说明:

// 如果存在多种有效的行程，你可以按字符自然排序返回最小的行程组合。例如，行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小，排序更靠前
// 所有的机场都用三个大写字母表示（机场代码）。
// 假定所有机票至少存在一种合理的行程。
// 示例 1:

// 输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
// 输出: ["JFK", "MUC", "LHR", "SFO", "SJC"]
// 示例 2:

// 输入: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
// 输出: ["JFK","ATL","JFK","SFO","ATL","SFO"]
// 解释: 另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。

#include <vector>
#include <string>
#include <unordered_map>
#include <queue>
#include <algorithm>

using namespace std;

/* Hierholzer 算法
化简本题题意：给定一个 n 个点 m 条边的图，要求从指定的顶点出发，
经过所有的边恰好一次（可以理解为给定起点的「一笔画」问题），使得路径的字典序最小。
定义：
    通过图中所有边恰好一次且行遍所有顶点的通路称为欧拉通路。
    通过图中所有边恰好一次且行遍所有顶点的回路称为欧拉回路。
    具有欧拉回路的无向图称为欧拉图。
    具有欧拉通路但不具有欧拉回路的无向图称为半欧拉图。
本题保证至少存在一种合理的路径，也就告诉了我们，这张图是一个欧拉图或者半欧拉图。
我们只需要输出这条欧拉通路的路径即可。
当我们贪心地选择字典序最小的节点前进时，我们可能先走入「死胡同」，从而导致无法遍历到其他还未访问的边。
于是我们希望能够遍历完当前节点所连接的其他节点后再进入「死胡同」。

Hierholzer 算法用于在连通图中寻找欧拉路径，其流程如下：
    从起点出发，进行深度优先搜索。
    每次沿着某条边从某个顶点移动到另外一个顶点的时候，都需要删除这条边。
    如果没有可移动的路径，则将所在节点加入到栈中，并返回。

当我们顺序地考虑该问题时，我们也许很难解决该问题，因为我们无法判断当前节点的哪一个分支是「死胡同」分支。
不妨倒过来思考。
我们注意到只有那个入度与出度差为 1 的节点会导致死胡同。而该节点必然是最后一个遍历到的节点。
我们可以改变入栈的规则，当我们遍历完一个节点所连的所有节点后，我们才将该节点入栈（即逆序入栈）。
对于当前节点而言，从它的每一个非「死胡同」分支出发进行深度优先搜索，都将会搜回到当前节点。
而从它的「死胡同」分支出发进行深度优先搜索将不会搜回到当前节点。
也就是说当前节点的死胡同分支将会优先于其他非「死胡同」分支入栈。
这样就能保证我们可以「一笔画」地走完所有边，最终的栈中逆序地保存了「一笔画」的结果。
我们只要将栈中的内容反转，即可得到答案。

时间复杂度：O(mlogm) m是边的数量
空间复杂度：O(m)
*/
class Solution {
public:
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        
        for (auto& ticket : tickets) {
            vec[ticket[0]].emplace(ticket[1]);
        }
        dfs("JFK");
        reverse(res.begin(), res.end());
        return res;
    }
    void dfs(const string& s) {
        while (vec.count(s) && vec[s].size() > 0) {
            string temp = vec[s].top();
            vec[s].pop();
            dfs(move(temp));
        }
        res.emplace_back(s);
    }
private:
    vector<string> res{}; // 栈
    unordered_map<string, priority_queue<string, vector<string>, std::greater<string>>> vec{}; // 最小堆
};